# -*- coding:utf-8 -*-
# @FileName : app\services\agent_service.py
# @Time     : 2025/11/28
# @Author   : 天空之城


import asyncio
import json
import time
from typing import List, Dict, Any, Optional

from app.services.mcp_server import MCPService
from app.services.model_factory import ModelFactory
from app.config import Config, logger
from app.models.conversation import MessageRole

# =============================================================================
# 常量定义
# =============================================================================

SYSTEM_PROMPT = """你是一个智能助手。请遵循以下原则：
1. **工具使用**：根据上下文判断是否需要工具。如果任务完成，必须调用 'finish_task' 或明确回复结束。
2. **思维链 (CoT)**：在行动前，请先进行一步步的思考分析。
3. **格式规范**：输出内容应清晰、条理分明。
"""
SUMMARY_PROMPT_TEMPLATE = """
你是对话历史总结专家。请阅读以下之前的对话记录，并生成一份精简的任务状态报告。

【之前的对话内容】：
{history_content}

【总结要求】：
请严格按照以下格式返回总结，不要包含多余废话：
---
1. ✅ **已完成步骤**：[简述已完成的操作和获得的关键结果]
2. ⏳ **待处理/剩余步骤**：[基于当前进度，推测后续还需要做什么]
3. 🔑 **关键上下文数据**：[保留后续步骤必须的ID、名称、文件路径、参数等具体信息]
---
"""


# =============================================================================
# AgentService 类定义
# =============================================================================

class AgentService:
    """
    智能体服务层：负责协调 LLM、记忆管理和 MCP 工具执行。
    """

    def __init__(self):
        # 初始化模型与工具服务
        self.model = ModelFactory.create_model("deepseek")
        self.mcp_service = MCPService(Config.MCP_SERVER_URL)

        # 运行时状态
        self.tool_execution_logs = {}

        # 配置参数
        self.max_iterations = getattr(Config, 'AGENT_MAX_ITERATIONS', 10)
        # 触发总结的字符阈值 (约等于 2000-3000 tokens)
        self.summary_trigger_limit = getattr(Config, 'SUMMARY_TRIGGER_LIMIT', 8000)
        # 必须保留的最近消息数量 (工作记忆窗口)，用于保护工具调用链
        self.safe_window_size = 6

        logger.info(f"AgentService initialized with model: {self.model.__class__.__name__}")

    def handle_user_message_stream(self, conversation_id: str, user_input: str, history_messages: list):
        """
        [SSE 核心] 修复版：确保步骤完整保存和流式输出
        """
        logger.info(f"开始处理对话 {conversation_id} 的用户消息，输入内容: {user_input[:100]}...")
        self.tool_execution_logs[conversation_id] = []

        # 初始化状态
        full_response_content = ""
        all_steps = []

        try:
            # 构建初始消息
            logger.info(f"为对话 {conversation_id} 构建初始消息上下文")
            base_messages = self._build_base_context(history_messages)
            messages = base_messages + [{"role": "user", "content": user_input}]
            logger.debug(f"对话 {conversation_id} 的初始消息构建完成，共 {len(messages)} 条消息")

            # 获取可用工具
            logger.info(f"为对话 {conversation_id} 初始化异步事件循环")
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)

            async def process_stream():
                nonlocal full_response_content, all_steps  # 添加 nonlocal 声明
                iteration = 0  # 将 iteration 移到内部定义

                try:
                    logger.info(f"为对话 {conversation_id} 建立 MCP 服务连接")
                    async with self.mcp_service as mcp:
                        logger.info(f"获取对话 {conversation_id} 的可用工具列表")
                        available_tools = await mcp.get_available_tools_schema()
                        logger.info(f"对话 {conversation_id} 获取到 {len(available_tools)} 个可用工具")

                        while iteration < self.max_iterations:
                            iteration += 1
                            logger.info(f"对话 {conversation_id} ReAct 循环迭代 {iteration}/{self.max_iterations}")

                            # Step A: 模型推理
                            try:
                                logger.info(f"对话 {conversation_id} 开始模型推理，迭代 {iteration}")
                                response = self.model.generate_response(
                                    messages=messages,
                                    tools=available_tools if available_tools else None
                                )
                                logger.info(f"对话 {conversation_id} 模型推理完成，迭代 {iteration}")
                            except Exception as e:
                                logger.error(f"对话 {conversation_id} 模型推理失败，迭代 {iteration}: {e}",
                                             exc_info=True)
                                # 推送错误事件
                                error_event = {
                                    "type": "error",
                                    "content": f"思考过程中断: {str(e)}",
                                    "step": iteration
                                }
                                error_event_json = json.dumps(error_event, ensure_ascii=False)
                                logger.debug(f"对话 {conversation_id} 推送错误事件: {error_event_json}")
                                yield error_event_json
                                return

                            response_msg = response.message
                            content = response_msg.content or ""
                            logger.debug(
                                f"对话 {conversation_id} 模型推理结果，迭代 {iteration}: 内容长度 {len(content)}, 工具调用数 {len(response_msg.tool_calls) if response_msg.tool_calls else 0}")

                            # Step B: 推送思考过程
                            if content:
                                thought_event = {
                                    "type": "thought",
                                    "step": iteration,
                                    "content": content
                                }
                                all_steps.append(thought_event)
                                thought_event_json = json.dumps(thought_event, ensure_ascii=False)
                                logger.debug(f"对话 {conversation_id} 推送思考过程事件: {thought_event_json[:100]}...")
                                yield thought_event_json

                            # Step C: 构建Assistant消息
                            logger.debug(f"对话 {conversation_id} 构建Assistant消息，迭代 {iteration}")
                            assistant_msg = self._create_assistant_message(content, response_msg.tool_calls)
                            messages.append(assistant_msg)
                            logger.debug(f"对话 {conversation_id} Assistant消息构建完成，迭代 {iteration}")

                            # Step D: 检查是否需要执行工具
                            if not response_msg.tool_calls:
                                logger.info(f"对话 {conversation_id} 不需要执行工具，迭代 {iteration}，返回最终回复")
                                full_response_content = content
                                # 推送最终回复
                                finish_event = {
                                    "type": "finish",
                                    "content": content,
                                    "step": iteration
                                }
                                finish_event_json = json.dumps(finish_event, ensure_ascii=False)
                                logger.debug(f"对话 {conversation_id} 推送最终回复事件: {finish_event_json[:100]}...")
                                yield finish_event_json
                                return

                            # Step E: 处理工具调用
                            logger.info(
                                f"对话 {conversation_id} 处理工具调用，迭代 {iteration}，共 {len(response_msg.tool_calls)} 个工具调用")

                            # 检查是否有 finish_task (特殊结束信号)
                            finish_call = next(
                                (tc for tc in response_msg.tool_calls if tc.function.name == "finish_task"), None)
                            if finish_call:
                                try:
                                    args = json.loads(
                                        finish_call.function.arguments) if finish_call.function.arguments else {}
                                    full_response_content = args.get("result", "任务完成")
                                    logger.info(
                                        f"对话 {conversation_id} 执行 finish_task，迭代 {iteration}，结果: {full_response_content[:100]}...")

                                    # 推送 finish_task 调用
                                    tool_call_event = {
                                        "type": "tool_call",
                                        "step": iteration,
                                        "tool_name": "finish_task",
                                        "arguments": args,
                                        "status": "running"
                                    }
                                    all_steps.append(tool_call_event)
                                    tool_call_event_json = json.dumps(tool_call_event, ensure_ascii=False)
                                    yield tool_call_event_json

                                    # 推送 finish_task 结果
                                    tool_result_event = {
                                        "type": "tool_result",
                                        "step": iteration,
                                        "tool_name": "finish_task",
                                        "result": full_response_content,
                                        "status": "completed"
                                    }
                                    all_steps.append(tool_result_event)
                                    tool_result_event_json = json.dumps(tool_result_event, ensure_ascii=False)
                                    yield tool_result_event_json

                                    # 将结果加入历史
                                    messages.append(self._create_tool_message(
                                        finish_call.id, "finish_task", "Task Finished"
                                    ))

                                    # 推送最终完成事件
                                    finish_event = {
                                        "type": "finish",
                                        "content": full_response_content,
                                        "step": iteration
                                    }
                                    finish_event_json = json.dumps(finish_event, ensure_ascii=False)
                                    yield finish_event_json
                                    return
                                except Exception as e:
                                    logger.error(f"对话 {conversation_id} 处理 finish_task 失败: {e}")

                            # 处理其他工具调用
                            for tool_call in response_msg.tool_calls:
                                # 跳过已经处理的 finish_task
                                if tool_call.function.name == "finish_task":
                                    continue

                                func_name = tool_call.function.name
                                logger.info(f"对话 {conversation_id} 处理工具调用: {func_name}，迭代 {iteration}")

                                # 推送工具调用开始
                                tool_call_event = {
                                    "type": "tool_call",
                                    "step": iteration,
                                    "tool_name": func_name,
                                    "arguments": json.loads(
                                        tool_call.function.arguments) if tool_call.function.arguments else {},
                                    "status": "running"
                                }
                                all_steps.append(tool_call_event)
                                tool_call_event_json = json.dumps(tool_call_event, ensure_ascii=False)
                                logger.debug(
                                    f"对话 {conversation_id} 推送工具调用事件: {tool_call_event_json[:100]}...")
                                yield tool_call_event_json

                                # 执行工具
                                tool_result = ""
                                status = "completed"
                                try:
                                    logger.info(f"对话 {conversation_id} 执行工具: {func_name}，迭代 {iteration}")
                                    result_obj = await mcp.execute_tool(
                                        func_name,
                                        json.loads(
                                            tool_call.function.arguments) if tool_call.function.arguments else {},
                                        conversation_id
                                    )
                                    tool_result = self._extract_text_from_mcp_result(result_obj)
                                    logger.info(
                                        f"对话 {conversation_id} 工具 {func_name} 执行完成，迭代 {iteration}，结果长度: {len(tool_result)}")
                                except Exception as e:
                                    status = "failed"
                                    tool_result = f"工具执行错误: {str(e)}"
                                    logger.error(
                                        f"对话 {conversation_id} 工具 {func_name} 执行失败，迭代 {iteration}: {e}",
                                        exc_info=True)

                                # 推送工具结果
                                tool_result_event = {
                                    "type": "tool_result",
                                    "step": iteration,
                                    "tool_name": func_name,
                                    "result": tool_result,
                                    "status": status
                                }
                                all_steps.append(tool_result_event)
                                tool_result_event_json = json.dumps(tool_result_event, ensure_ascii=False)
                                logger.debug(
                                    f"对话 {conversation_id} 推送工具结果事件: {tool_result_event_json[:100]}...")
                                yield tool_result_event_json

                                # 将结果加入历史
                                logger.debug(f"对话 {conversation_id} 将工具结果加入历史，迭代 {iteration}")
                                messages.append(self._create_tool_message(
                                    tool_call.id, func_name, tool_result
                                ))

                except Exception as e:
                    logger.error(f"对话 {conversation_id} 异步处理过程中发生错误: {e}", exc_info=True)
                    error_event = {
                        "type": "error",
                        "content": f"处理过程中发生错误: {str(e)}",
                        "step": iteration
                    }
                    error_event_json = json.dumps(error_event, ensure_ascii=False)
                    yield error_event_json

            # 执行异步流处理
            logger.info(f"开始执行对话 {conversation_id} 的异步流处理")

            # 辅助函数：将异步生成器转换为同步生成器
            def async_generator_to_sync():
                async_gen = process_stream()
                try:
                    while True:
                        try:
                            chunk = loop.run_until_complete(async_gen.__anext__())
                            yield chunk
                        except StopAsyncIteration:
                            break
                except Exception as e:
                    logger.error(f"对话 {conversation_id} 异步生成器转换失败: {e}")
                    error_event = {
                        "type": "error",
                        "content": f"流处理失败: {str(e)}",
                        "step": 0
                    }
                    yield json.dumps(error_event, ensure_ascii=False)

            # 返回同步生成器
            for chunk in async_generator_to_sync():
                yield chunk

        except Exception as e:
            logger.error(f"对话 {conversation_id} 处理过程中发生错误: {e}", exc_info=True)
            error_message = str(e)
            if "MCP客户端" in error_message or "未连接到" in error_message:
                error_event = {
                    "type": "error",
                    "content": f"【MCP客户端】{error_message}",
                    "step": 0
                }
            else:
                error_event = {
                    "type": "error",
                    "content": f"处理过程中发生错误: {error_message}",
                    "step": 0
                }
            error_event_json = json.dumps(error_event, ensure_ascii=False)
            yield error_event_json

        finally:
            # 【关键】保存完整记录到数据库
            logger.info(f"对话 {conversation_id} 开始保存最终消息到数据库")
            try:
                if not full_response_content and all_steps:
                    # 如果没有最终内容，但从步骤中提取最后的结果
                    logger.info(f"对话 {conversation_id} 从步骤中提取最终结果")
                    for step in reversed(all_steps):
                        if step.get("type") == "tool_result" and step.get("tool_name") == "finish_task":
                            full_response_content = step.get("result", "任务完成")
                            logger.info(
                                f"对话 {conversation_id} 从 finish_task 步骤中提取到最终结果: {full_response_content[:100]}...")
                            break
                        elif step.get("type") == "thought" and step.get("content"):
                            # 如果没有 finish_task，使用最后的思考内容
                            full_response_content = step.get("content")
                            logger.info(
                                f"对话 {conversation_id} 从思考步骤中提取到最终结果: {full_response_content[:100]}...")
                            break

                logger.info(
                    f"对话 {conversation_id} 保存最终消息到数据库，内容长度: {len(full_response_content)}, 步骤数: {len(all_steps)}")
                self._save_final_message_to_db(conversation_id, full_response_content, all_steps)
                logger.info(f"对话 {conversation_id} 最终消息保存到数据库完成")
            except Exception as save_error:
                logger.error(f"对话 {conversation_id} 保存消息到数据库失败: {save_error}", exc_info=True)

            # 清理资源
            logger.info(f"对话 {conversation_id} 开始清理资源")
            try:
                logger.debug(f"对话 {conversation_id} 断开 MCP 服务连接")
                loop.run_until_complete(self.mcp_service.disconnect())
                logger.debug(f"对话 {conversation_id} MCP 服务连接已断开")
            except Exception as e:
                logger.error(f"对话 {conversation_id} 断开 MCP 服务连接失败: {e}")
            finally:
                logger.debug(f"对话 {conversation_id} 关闭事件循环")
                loop.close()
                logger.info(f"对话 {conversation_id} 资源清理完成")

    def _async_generator_to_sync(self, async_gen, loop):
        """
        将异步生成器转换为同步生成器
        """
        try:
            while True:
                try:
                    chunk = loop.run_until_complete(async_gen.__anext__())
                    yield chunk
                except StopAsyncIteration:
                    break
        except Exception as e:
            logger.error(f"异步生成器转换失败: {e}")
            error_event = {
                "type": "error",
                "content": f"流处理失败: {str(e)}",
                "step": 0
            }
            yield json.dumps(error_event, ensure_ascii=False)

    async def _process_async(self, conversation_id: str, user_input: str, raw_history: list, save_step_callback=None):
        """
        [异步核心] 包含：上下文构建 -> 记忆压缩 -> ReAct 循环
        """
        async with self.mcp_service as mcp:
            # 1. 获取可用工具
            available_tools = await mcp.get_available_tools_schema()

            # 2. 上下文准备：清洗历史数据
            base_messages = self._build_base_context(raw_history)

            # 3. 记忆压缩：如果历史太长，执行智能总结
            messages = await self._compress_history_with_summary(base_messages, user_input)

            # 4. 添加当前用户的新指令
            messages.append({"role": "user", "content": user_input})

            # --- 进入 ReAct 循环 ---
            iteration = 0
            final_content = ""
            execution_steps = []

            while iteration < self.max_iterations:
                iteration += 1
                logger.debug(f"ReAct Loop Iteration {iteration}/{self.max_iterations}")

                # Step A: 模型推理
                try:
                    response = self.model.generate_response(
                        messages=messages,
                        tools=available_tools if available_tools else None
                    )
                except Exception as e:
                    logger.error(f"LLM generation failed: {e}")
                    final_content = "思考过程中断，请重试。"
                    step_data = {
                        "step": iteration,
                        "type": "thought",
                        "content": f"思考过程中断: {str(e)}"
                    }
                    execution_steps.append(step_data)
                    # 实时保存步骤到数据库
                    if save_step_callback:
                        save_step_callback(step_data)
                    break

                response_msg = response.message
                content = response_msg.content or ""

                # Step B: 构造并保存 Assistant 消息
                assistant_msg = self._create_assistant_message(content, response_msg.tool_calls)
                messages.append(assistant_msg)

                # 记录思考过程
                if content:
                    step_data = {
                        "step": iteration,
                        "type": "thought",
                        "content": content
                    }
                    execution_steps.append(step_data)
                    # 实时保存步骤到数据库
                    if save_step_callback:
                        save_step_callback(step_data)

                # Step C: 检查是否需要执行工具
                if not response_msg.tool_calls:
                    final_content = content
                    logger.info("No tool calls, loop finished.")
                    break

                # Step D: 检查是否有 finish_task (特殊结束信号)
                finish_call = next((tc for tc in response_msg.tool_calls if tc.function.name == "finish_task"), None)
                if finish_call:
                    final_content = self._extract_finish_result(finish_call)
                    # 补全历史，保持对话完整性
                    messages.append(self._create_tool_message(finish_call.id, "finish_task", "Task Finished"))

                    # 记录 finish_task
                    step_data = {
                        "step": iteration,
                        "type": "tool_call",
                        "tool_name": "finish_task",
                        "result": final_content
                    }
                    execution_steps.append(step_data)
                    # 实时保存步骤到数据库
                    if save_step_callback:
                        save_step_callback(step_data)
                    break

                # Step E: 执行工具 (串行或并行)
                for tool_call in response_msg.tool_calls:
                    # 跳过 finish_task，因为上面已经处理了
                    if tool_call.function.name == "finish_task":
                        continue

                    # 记录工具调用
                    tool_args = json.loads(tool_call.function.arguments) if tool_call.function.arguments else {}
                    step_data = {
                        "step": iteration,
                        "type": "tool_call",
                        "tool_name": tool_call.function.name,
                        "arguments": tool_args
                    }
                    execution_steps.append(step_data)
                    # 实时保存步骤到数据库
                    if save_step_callback:
                        save_step_callback(step_data)

                    # 执行并记录日志
                    tool_result_content = await self._execute_single_tool(
                        conversation_id, mcp, tool_call, iteration
                    )

                    # 将结果加入历史
                    messages.append(self._create_tool_message(
                        tool_call.id,
                        tool_call.function.name,
                        tool_result_content
                    ))

                    # 记录工具执行结果
                    step_data = {
                        "step": iteration,
                        "type": "tool_result",
                        "tool_name": tool_call.function.name,
                        "result": tool_result_content
                    }
                    execution_steps.append(step_data)
                    # 实时保存步骤到数据库
                    if save_step_callback:
                        save_step_callback(step_data)

            # 循环结束后的兜底
            if not final_content:
                # 如果最后一条是 Assistant 的文本，就用它
                if messages and messages[-1]["role"] == "assistant" and messages[-1].get("content"):
                    final_content = messages[-1]["content"]
                else:
                    final_content = "任务已达到最大步数，已停止执行。"

            return {"content": final_content, "execution_id": None, "execution_steps": execution_steps}

    # =========================================================================
    #  核心功能：记忆压缩与总结
    # =========================================================================

    async def _compress_history_with_summary(self, history_messages: List[Dict], user_input: str) -> List[Dict]:
        """
        如果上下文超出限制，则将旧消息总结为摘要，保留 System Prompt 和最近的消息(Safe Window)。
        """
        system_msg = history_messages[0]  # 假设第一条总是 System

        # 估算总长度
        total_chars = sum(len(str(m)) for m in history_messages) + len(user_input)

        if total_chars < self.summary_trigger_limit:
            return history_messages

        logger.info(f"Context length ({total_chars}) exceeds limit. Triggering summarization.")

        # --- 计算切分点 ---
        # 至少保留最近 N 条，且不能从 Tool 消息中间切断
        if len(history_messages) <= self.safe_window_size + 1:
            return history_messages

        cut_index = len(history_messages) - self.safe_window_size

        # 【关键】安全窗口回溯：如果切分点是 tool 消息，必须向前找到它的 assistant
        # 避免出现孤儿 Tool Result
        while cut_index > 1:
            role = history_messages[cut_index].get("role")
            if role == "tool":
                cut_index -= 1
            else:
                # 找到了 safe window 的起始点 (通常是 assistant 或 user)
                break

        # 拆分：Body (待总结) vs Tail (保留)
        body_messages = history_messages[1:cut_index]
        tail_messages = history_messages[cut_index:]

        if not body_messages:
            return history_messages

        # --- 执行总结 ---
        logger.info(f"Summarizing {len(body_messages)} old messages...")
        summary_text = await self._generate_history_summary(body_messages)

        summary_message = {
            "role": "user",  # 使用 User 角色注入背景，效果通常比 System 好
            "content": f"【系统历史摘要】\n由于对话过长，之前的详细记录已被压缩。请基于以下状态继续执行：\n{summary_text}"
        }

        # 重组：System + Summary + Safe Window
        return [system_msg, summary_message] + tail_messages

    async def _generate_history_summary(self, messages: List[Dict]) -> str:
        """
        调用 LLM 生成摘要
        """
        try:
            # 序列化历史，并做长度截断防止 Token 溢出
            history_str = json.dumps(messages, ensure_ascii=False)
            if len(history_str) > self.summary_trigger_limit:
                history_str = history_str[-int(self.summary_trigger_limit):]

            prompt = SUMMARY_PROMPT_TEMPLATE.format(history_content=history_str)

            # 使用无 Tools 的纯对话模式
            response = self.model.generate_response(
                messages=[{"role": "user", "content": prompt}]
            )
            return response.message.content or "（总结生成失败）"
        except Exception as e:
            logger.error(f"Summary generation failed: {e}")
            return "（无法生成历史摘要，请忽略此部分）"

    # =========================================================================
    #  辅助功能：数据清洗与构造
    # =========================================================================

    def _build_base_context(self, raw_history: list) -> List[Dict]:
        """从原始数据构建标准消息列表，包含 System Prompt"""
        messages = [{"role": "system", "content": SYSTEM_PROMPT}]
        for msg in raw_history:
            cleaned = self._restore_message_structure(msg)
            if cleaned:
                messages.append(cleaned)
        return messages

    def _restore_message_structure(self, msg: Dict) -> Optional[Dict]:
        """
        清洗单条消息：处理 DB 序列化问题，还原 tool_calls 对象
        """
        role = msg.get("role")
        content = msg.get("content")
        new_msg = {
            "role": role,
            "content": content if content is not None else ""
        }

        # 1. 还原 Assistant 的 tool_calls
        if role == "assistant":
            raw_calls = msg.get("tool_calls")
            if raw_calls:
                # 如果是字符串 (DB 读取)，尝试解析
                if isinstance(raw_calls, str):
                    try:
                        raw_calls = json.loads(raw_calls)
                    except json.JSONDecodeError:
                        logger.warning("Failed to parse tool_calls string")
                        raw_calls = []

                # 确保是列表格式
                if isinstance(raw_calls, list) and raw_calls:
                    new_msg["tool_calls"] = raw_calls

        # 2. 还原 Tool 消息
        elif role == "tool":
            tool_id = msg.get("tool_call_id")
            if not tool_id:
                # 没有 ID 的 Tool 消息是非法的，必须丢弃以免 API 报错
                return None
            new_msg["tool_call_id"] = tool_id
            new_msg["name"] = msg.get("name", "unknown_tool")

        return new_msg

    def _create_assistant_message(self, content: str, tool_calls: List) -> Dict:
        """标准化 Assistant 消息构造"""
        msg = {"role": "assistant", "content": content}
        if tool_calls:
            msg["tool_calls"] = [
                {
                    "id": tc.id,
                    "type": "function",
                    "function": {
                        "name": tc.function.name,
                        "arguments": tc.function.arguments
                    }
                } for tc in tool_calls
            ]
        return msg

    def _create_tool_message(self, call_id: str, name: str, content: str) -> Dict:
        """标准化 Tool 消息构造"""
        return {
            "role": "tool",
            "tool_call_id": call_id,
            "name": name,
            "content": content
        }

    # =========================================================================
    #  工具执行与日志
    # =========================================================================

    async def _execute_single_tool(self, conversation_id: str, mcp: MCPService, tool_call: Any, step: int) -> str:
        """执行单个工具，处理参数解析、调用和结果格式化"""
        func_name = tool_call.function.name
        args_str = tool_call.function.arguments

        # 1. 解析参数
        try:
            args = json.loads(args_str)
        except json.JSONDecodeError:
            args = {"raw_args": args_str}

        # 2. 记录开始日志
        self._log_tool(conversation_id, step, func_name, args, "started")

        status = "completed"
        result_text = ""

        # 3. 执行 MCP 调用
        try:
            logger.info(f"Calling Tool: {func_name} | ID: {conversation_id}")
            mcp_result = await mcp.execute_tool(
                tool_name=func_name,
                params=args,
                task_id=conversation_id
            )

            # 提取结果文本
            result_text = self._extract_text_from_mcp_result(mcp_result)

        except Exception as e:
            status = "failed"
            result_text = f"Tool execution error: {str(e)}"
            logger.error(f"Tool {func_name} failed: {e}")

        # 4. 记录结束日志
        self._log_tool(conversation_id, step, func_name, args, status, result_text)

        return result_text

    def _extract_text_from_mcp_result(self, result: Any) -> str:
        """从 MCP 复杂返回对象中提取纯文本"""
        if isinstance(result, str):
            return result

        # 适配常见的 MCP SDK CallToolResult 结构
        content_parts = []
        if hasattr(result, 'content') and isinstance(result.content, list):
            for item in result.content:
                if hasattr(item, 'text') and item.text:
                    content_parts.append(item.text)
                elif isinstance(item, dict) and item.get('type') == 'text':
                    content_parts.append(item.get('text', ''))

        if content_parts:
            return "\n".join(content_parts)

        return str(result)

    def _extract_finish_result(self, tool_call) -> str:
        """从 finish_task 工具中提取最终答案"""
        try:
            args = json.loads(tool_call.function.arguments)
            return args.get("result", args.get("reason", "任务完成"))
        except:
            return "任务完成"

    def _log_tool(self, conversation_id: str, step: int, name: str, args: dict, status: str, result: str = None):
        """统一日志记录"""
        entry = {
            "step": step,
            "tool_name": name,
            "arguments": args,
            "status": status,
            "timestamp": time.time()
        }
        if result:
            # 结果预览，防止日志过大
            entry["result_preview"] = result[:200] + "..." if len(result) > 200 else result

        if conversation_id in self.tool_execution_logs:
            self.tool_execution_logs[conversation_id].append(entry)

    def _save_final_message_to_db(self, conversation_id, content, steps):
        """
        将最终的 AI 回复和步骤保存到数据库
        
        Args:
            conversation_id: 对话ID
            content: AI 回复内容
            steps: 工具调用步骤列表
        """
        try:
            # 局部引用避免循环依赖
            from app.api.v1.conversations.services import MessageService
            message_service = MessageService()

            # 构造 meta_data
            meta_data = {
                "steps": steps,
                "model": self.model.__class__.__name__
            }

            # 保存到数据库
            message_service.create_message(
                conversation_id=int(conversation_id),
                role=MessageRole.ASSISTANT,
                content=content,
                meta_data=meta_data
            )
            logger.info(f"Conversation {conversation_id} AI response saved to DB.")
        except Exception as e:
            logger.error(f"Failed to save AI message to DB: {e}")
